/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.util.geom;

/**
 * Utils to determine if location of a point in a rectangle, center points,
 * 
 * @author kentelt
 * 
 */
public final class GeomUtil {

	/**
	 * Retrieves the center position of all numbers
	 * 
	 * @param n1
	 *            first number
	 * @param n2
	 *            second number
	 * @param n3
	 *            third number
	 * @return the center of all numbers: min( n1, n2, n3 ) + (max( n1, n2, n3 )
	 *         - min( n1, n2, n3 )) / 2
	 */
	public static int getCenter(int n1, int n2, int n3) {
		int max = Math.max(n1, Math.max(n2, n3));
		int min = Math.min(n1, Math.min(n2, n3));
		return min + ((max - min) / 2);
	}

	/**
	 * Determine if rectangles R1, R2 intersect.
	 * 
	 * @param r1
	 * @param r2
	 * @return
	 */
	public static boolean intersects(Rectangle r1, Rectangle r2) {
		int r1Width = r1.width;
		int r1Height = r1.height;
		int r2Width = r2.width;
		int r2Height = r2.height;
		if (r2Width <= 0 || r2Height <= 0 || r1Width <= 0 || r1Height <= 0) {
			return false;
		} else {
			r2Width += r2.x;
			r2Height += r2.y;
			r1Width += r1.x;
			r1Height += r1.y;
			return (r2Width < r2.x || r2Width > r1.x) && (r2Height < r2.y || r2Height > r1.y)
					&& (r1Width < r1.x || r1Width > r2.x) && (r1Height < r1.y || r1Height > r2.y);
		}

	}

	/**
	 * Checks if rectangle r1 is inside rectangle r2
	 * 
	 * @param r1X
	 * @param r1Y
	 * @param r1Width
	 * @param r1Height
	 * @param r2X
	 * @param r2Y
	 * @param r2Width
	 * @param r2Height
	 * @return
	 */
	public static boolean isInside(final int r1X, final int r1Y, int r1Width, int r1Height, int r2X, int r2Y,
			int r2Width, int r2Height) {
		if ((r2Width | r2Height | r1Width | r1Height) < 0)
			return false;
		if (r1X < r2X || r1Y < r2Y)
			return false;
		r2Width += r2X;
		r1Width += r1X;
		if (r1Width <= r1X) {
			if (r2Width >= r2X || r1Width > r2Width)
				return false;
		} else if (r2Width >= r2X && r1Width > r2Width)
			return false;
		r2Height += r2Y;
		r1Height += r1Y;
		if (r1Height <= r1Y) {
			if (r2Height >= r2Y || r1Height > r2Height)
				return false;
		} else if (r2Height >= r2Y && r1Height > r2Height)
			return false;
		return true;
	}

	/**
	 * Check if rectangle r1 is inside r2
	 * 
	 * @param r1
	 * @param r2
	 * @return
	 */
	public static boolean isInside(Rectangle r1, Rectangle r2) {
		return isInside(r1.x, r1.y, r1.width, r1.height, r2.x, r2.y, r2.width, r2.height);
	}

	private static boolean sameSide(int p1x, int p1y, int p2x, int p2y, int l1x, int l1y, int l2x, int l2y) {
		long lhs = ((p1x - l1x) * (l2y - l1y) - (l2x - l1x) * (p1y - l1y));
		long rhs = ((p2x - l1x) * (l2y - l1y) - (l2x - l1x) * (p2y - l1y));
		long product = lhs * rhs;
		boolean result = product >= 0;
		return result;
	}

	/**
	 * Union of rectangle r1 and r2
	 * 
	 * @param r1
	 * @param r2
	 * @return
	 */
	public static Rectangle union(Rectangle r1, Rectangle r2) {
		long l = r1.width;
		long l1 = r1.height;
		if ((l | l1) < 0L)
			return new Rectangle(r2);
		long l2 = r2.width;
		long l3 = r2.height;
		if ((l2 | l3) < 0L)
			return new Rectangle(r1);
		int i = r1.x;
		int j = r1.y;
		l += i;
		l1 += j;
		int k = r2.x;
		int i1 = r2.y;
		l2 += k;
		l3 += i1;
		if (i > k)
			i = k;
		if (j > i1)
			j = i1;
		if (l < l2)
			l = l2;
		if (l1 < l3)
			l1 = l3;
		l -= i;
		l1 -= j;
		if (l > 0x7fffffffL)
			l = 0x7fffffffL;
		if (l1 > 0x7fffffffL)
			l1 = 0x7fffffffL;
		return new Rectangle(i, j, (int) l, (int) l1);
	}

	/**
	 * Checks if point px,py is within the bounds of the rectangle
	 * 
	 * @param px
	 *            x coordinate of the point to be checked
	 * @param py
	 *            y coordinate of the point to be checked
	 * @param x
	 *            point of the rectangle
	 * @param y
	 *            point of the rectangle
	 * @param width
	 *            of the rectangle
	 * @param height
	 *            of the rectangle
	 * @return
	 */
	public static boolean withinBounds(final int px, final int py, final int x, final int y, int width, int height) {
		if ((width | height) < 0)
			return false;
		if (px < x || py < y) {
			return false;
		} else {
			width += x;
			height += y;
			return (width < x || width > px) && (height < y || height > py);
		}
	}

	/**
	 * Checks whether the specified point px, py is within the triangle defined
	 * by ax, ay, bx, by and cx, cy.
	 * 
	 * @param px
	 *            The x of the point to test
	 * @param py
	 *            The y of the point to test
	 * @param ax
	 *            The x of the 1st point of the triangle
	 * @param ay
	 *            The y of the 1st point of the triangle
	 * @param bx
	 *            The x of the 2nd point of the triangle
	 * @param by
	 *            The y of the 2nd point of the triangle
	 * @param cx
	 *            The x of the 3rd point of the triangle
	 * @param cy
	 *            The y of the 3rd point of the triangle
	 * @return true when the point is within the given triangle
	 */
	public static boolean withinBounds(int px, int py, int ax, int ay, int bx, int by, int cx, int cy) {
		if (px < Math.min(ax, Math.min(bx, cx)) || px > Math.max(ax, Math.max(bx, cx))
				|| py < Math.min(ay, Math.min(by, cy)) || py > Math.max(ay, Math.max(by, cy))) {
			return false;
		}
		boolean sameabc = sameSide(px, py, ax, ay, bx, by, cx, cy);
		boolean samebac = sameSide(px, py, bx, by, ax, ay, cx, cy);
		boolean samecab = sameSide(px, py, cx, cy, ax, ay, bx, by);
		return sameabc && samebac && samecab;
	}

	/**
	 * Checks if <code>point</code> is within the bounds of the rectangle
	 * 
	 * @param point
	 * @param rect
	 * @return
	 */
	public static boolean withinBounds(Point point, Rectangle rect) {
		return withinBounds(point.getX(), point.getY(), rect.x, rect.y, rect.width, rect.height);
	}

}
